home *** CD-ROM | disk | FTP | other *** search
/ PC World 2007 December / PCWorld_2007-12_cd.bin / v cisle / htttrack / httrack-3.41-3.exe / {app} / src / proxy / proxytrack.c < prev    next >
C/C++ Source or Header  |  2006-08-19  |  46KB  |  1,590 lines

  1. /* ------------------------------------------------------------ */
  2. /*
  3. HTTrack Website Copier, Offline Browser for Windows and Unix
  4. Copyright (C) Xavier Roche and other contributors
  5.  
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  19.  
  20. Please visit our Website: http://www.httrack.com
  21. */
  22.  
  23. /* ------------------------------------------------------------ */
  24. /* File: ProxyTrack, httrack cache-based proxy                  */
  25. /* Author: Xavier Roche                                         */
  26. /* ------------------------------------------------------------ */
  27.  
  28.  
  29. /*
  30.  
  31. /\/\/\/\/\/\/\/\/\/\/\/\/\ PENDING WORK /\/\/\/\/\/\/\/\/\/\/\/\/\
  32. - Etag update handling
  33. - Other cache archive handling (.arc)
  34. - Live plug/unplug of archives
  35. - Listing
  36. \/\/\/\/\/\/\/\/\/\/\/\/\/ PENDING WORK \/\/\/\/\/\/\/\/\/\/\/\/\/
  37.  
  38. */
  39.  
  40. /*
  41. Architecture rough draft
  42. Xavier Roche 2005
  43.  
  44. Aim: Building a sub-proxy to be linked with other top level proxies (such as Squid)
  45. Basic design: Classical HTTP/1.0 proxy server, with ICP server support
  46. Internal data design: HTTrack cache indexing in fast hashtables, with 'pluggable' design (add/removal of caches on-the-fly)
  47.  
  48.  
  49. Index structure organization:
  50. -----------------------------
  51.  
  52. foo/hts-cache/new.zip    -----> Index[0]   \
  53. bar/hts-cache/new.zip   -----> Index[1]  >  Central Index Lookup (CIL)
  54. baz/hts-cache/new.zip   -----> Index[2] /
  55. ..            -----> ..
  56.  
  57. Indexes are hashtables with URL (STRING) -> INTEGER lookup.
  58.  
  59. URL -----> CIL            Ask for index ID
  60. URL -----> Index[ID]        Ask for index properties (ZIP cache index)
  61.  
  62.  
  63. Lookup of an entry:
  64. -------------------
  65.  
  66. ID = CIL[URL]
  67. If ID is valid Then
  68.     return SUCCESS
  69. Else
  70.     return FAILURE
  71. EndIf
  72.  
  73.  
  74. Fetching of an entry:
  75. ---------------------
  76.  
  77. RESOURCE = null
  78. ID = CIL[URL]
  79. If ID is valid Then
  80.     OFFSET = Index[ID][URL]
  81.     If OFFSET is valid Then
  82.         RESOURCE = Fetch(ID, OFFSET)
  83.     EndIf
  84. EndIf
  85.  
  86.  
  87. Removal of index N:
  88. -------------------
  89.  
  90. For all entries in Index[N]
  91.     URL(key) -----> Lookup all other caches
  92.         Found: Replace in CIL
  93.         Not Found: Delete entry in CIL
  94. Done
  95. Delete Index[N]
  96.  
  97.  
  98. Adding of index N:
  99. ------------------
  100.  
  101. Build Index[N]
  102. For all entries in Index[N]
  103.     URL(key) -----> Lookup in CIL
  104.         Found: Do nothing if corresponding Cache is newer than this one
  105.         Not Found: Add/Replace entry in CIL
  106. Done
  107.  
  108. Remark: If no cache newer than the added one is found, all entries can be added without any lookup (optim)
  109.  
  110. */
  111.  
  112. /* HTTrack definitions */
  113. #include "htsbase.h"
  114. #include "htsnet.h"
  115. #include "htslib.h"
  116. #include "htsglobal.h"
  117. #include <stdio.h>
  118. #include <stdlib.h>
  119. #include <string.h>
  120. #include <time.h>
  121. #include <fcntl.h>
  122. #ifdef _WIN32
  123. #else
  124. #include <arpa/inet.h>
  125. #endif
  126. /* END specific definitions */
  127.  
  128. /* String */
  129. #include "proxystrings.h"
  130.  
  131. /* Network base */
  132. #include "htsbasenet.h"
  133.  
  134. /* dΘfinitions globales */
  135. #include "htsglobal.h"
  136.  
  137. /* htsweb */
  138. #include "htsinthash.h"
  139.  
  140. /* ProxyTrack */
  141. #include "proxytrack.h"
  142.  
  143. /* Store manager */
  144. #include "../minizip/mztools.h"
  145. #include "store.h"
  146.  
  147. /* threads */
  148. #ifdef _WIN32
  149. #include <process.h>    /* _beginthread, _endthread */
  150. #else
  151. #include <pthread.h>
  152. #endif
  153.  
  154. /* External references */
  155. // htsErrorCallback htsCallbackErr = NULL;
  156. int htsMemoryFastXfr = 1;    /* fast xfr by default */
  157. void abortLog__fnc(char* msg, char* file, int line);
  158. void abortLog__fnc(char* msg, char* file, int line) {
  159.   FILE* fp = fopen("CRASH.TXT", "wb");
  160.   if (!fp) fp = fopen("/tmp/CRASH.TXT", "wb");
  161.   if (!fp) fp = fopen("C:\\CRASH.TXT", "wb");
  162.   if (!fp) fp = fopen("CRASH.TXT", "wb");
  163.   if (fp) {
  164.     fprintf(fp, "HTTrack " HTTRACK_VERSIONID " closed at '%s', line %d\r\n", file, line);
  165.     fprintf(fp, "Reason:\r\n%s\r\n", msg);
  166.     fflush(fp);
  167.     fclose(fp);
  168.   }
  169. }
  170. // HTSEXT_API t_abortLog abortLog__ = abortLog__fnc;    /* avoid VC++ inlining */
  171. #define webhttrack_lock(A) do{}while(0)
  172.  
  173. /* Static definitions */
  174.  
  175. static int linputsoc(T_SOC soc, char* s, int max) {
  176.   int c;
  177.   int j=0;
  178.   do {
  179.     unsigned char ch;
  180.     if (recv(soc, &ch, 1, 0) == 1) {
  181.       c = ch;
  182.     } else {
  183.       c = EOF;
  184.     }
  185.     if (c!=EOF) {
  186.       switch(c) {
  187.         case 13: break;  // sauter CR
  188.         case 10: c=-1; break;
  189.         case 9: case 12: break;  // sauter ces caractΦres
  190.         default: s[j++]=(char) c; break;
  191.       }
  192.     }
  193.   }  while((c!=-1) && (c!=EOF) && (j<(max-1)));
  194.   s[j]='\0';
  195.   return j;
  196. }
  197.  
  198. static int check_readinput_t(T_SOC soc, int timeout) {
  199.   if (soc != INVALID_SOCKET) {
  200.     fd_set fds;           // poll structures
  201.     struct timeval tv;          // structure for select
  202.     FD_ZERO(&fds);
  203.     FD_SET(soc,&fds);           
  204.     tv.tv_sec=timeout;
  205.     tv.tv_usec=0;
  206.     select((int)(soc + 1),&fds,NULL,NULL,&tv);
  207.     if (FD_ISSET(soc,&fds))
  208.       return 1;
  209.     else
  210.       return 0;
  211.   } else
  212.     return 0;
  213. }
  214.  
  215. static int linputsoc_t(T_SOC soc, char* s, int max, int timeout) {
  216.   if (check_readinput_t(soc, timeout)) {
  217.     return linputsoc(soc, s, max);
  218.   }
  219.   return -1;
  220. }
  221.  
  222. static int gethost(const char* hostname, SOCaddr *server, size_t server_size) {
  223.   if (hostname != NULL && *hostname != '\0') {
  224. #if HTS_INET6==0
  225.         /*
  226.         ipV4 resolver
  227.         */
  228.         t_hostent* hp=gethostbyname(hostname);
  229.         if (hp!=NULL) {
  230.             if (hp->h_length) {
  231.                 SOCaddr_copyaddr(*server, server_size, hp->h_addr_list[0], hp->h_length);
  232.                 return 1;
  233.             }
  234.         }
  235. #else
  236.         /*
  237.         ipV6 resolver
  238.         */
  239.         struct addrinfo* res = NULL;
  240.         struct addrinfo hints;
  241.         memset(&hints, 0, sizeof(hints));
  242. #if 0
  243.         if (IPV6_resolver == 1)        // V4 only (for bogus V6 entries)
  244.             hints.ai_family = PF_INET;
  245.         else if (IPV6_resolver == 2)   // V6 only (for testing V6 only)
  246.             hints.ai_family = PF_INET6;
  247.         else
  248. #endif
  249.         hints.ai_family = PF_UNSPEC;
  250.         hints.ai_socktype = SOCK_STREAM;
  251.         hints.ai_protocol = IPPROTO_TCP;
  252.         if (getaddrinfo(hostname, NULL, &hints, &res) == 0) {
  253.             if (res) {
  254.                 if ( (res->ai_addr) && (res->ai_addrlen) ) {
  255.                     SOCaddr_copyaddr(*server, server_size, res->ai_addr, res->ai_addrlen);
  256.                     freeaddrinfo(res);
  257.                     return 1;
  258.                 }
  259.             }
  260.         }
  261.         if (res) {
  262.             freeaddrinfo(res);
  263.         }
  264.  
  265. #endif
  266.     }
  267.     return 0;
  268. }
  269.  
  270. static String getip(SOCaddr *server, int serverLen) {
  271.     String s = STRING_EMPTY;
  272. #if HTS_INET6==0
  273.     unsigned int sizeMax = sizeof("999.999.999.999:65535");
  274. #else
  275.     unsigned int sizeMax = sizeof("ffff:ffff:ffff:ffff:ffff:ffff:ffff:65535");
  276. #endif
  277.     char * dotted = malloc(sizeMax + 1);
  278.     unsigned short port  = ntohs(SOCaddr_sinport(*server));
  279.     if (dotted == NULL) {
  280.         CRITICAL("memory exhausted");
  281.         return s;
  282.     }
  283.     SOCaddr_inetntoa(dotted, sizeMax, *server, serverLen);
  284.     sprintf(dotted + strlen(dotted), ":%d", port);
  285.     StringAttach(&s, &dotted);
  286.     return s;
  287. }
  288.  
  289.  
  290. static T_SOC smallserver_init(const char* adr, int port, int family) {
  291.     SOCaddr server;
  292.     size_t server_size = sizeof(server);
  293.  
  294.     memset(&server, 0, sizeof(server));
  295.     SOCaddr_initany(server, server_size); 
  296.   if (gethost(adr, &server, server_size)) {    // host name
  297.         T_SOC soc = INVALID_SOCKET;
  298.     if ( (soc = (T_SOC) socket(SOCaddr_sinfamily(server), family, 0)) != INVALID_SOCKET) {
  299.       SOCaddr_initport(server, port);
  300.       if ( bind(soc,(struct sockaddr*) &server, (int)server_size) == 0 ) {
  301.         if (family != SOCK_STREAM 
  302.                     || listen(soc, 10) >=0 ) {
  303.                     return soc;
  304.         } else {
  305. #ifdef _WIN32
  306.           closesocket(soc);
  307. #else
  308.           close(soc);
  309. #endif
  310.           soc=INVALID_SOCKET;
  311.         }
  312.       } else {
  313. #ifdef _WIN32
  314.         closesocket(soc);
  315. #else
  316.         close(soc);
  317. #endif
  318.         soc=INVALID_SOCKET;
  319.       }
  320.     }
  321.   }
  322.   return INVALID_SOCKET;
  323. }
  324.  
  325. static int proxytrack_start(PT_Indexes indexes, T_SOC soc, T_SOC socICP);
  326. int proxytrack_main(char* proxyAddr, int proxyPort, 
  327.                                         char* icpAddr, int icpPort, 
  328.                                         PT_Indexes index) {
  329.   int returncode = 0;
  330.   T_SOC soc = smallserver_init(proxyAddr, proxyPort, SOCK_STREAM);
  331.   T_SOC socICP = smallserver_init(proxyAddr, icpPort, SOCK_DGRAM);
  332.   if (soc != INVALID_SOCKET
  333.         && socICP != INVALID_SOCKET) 
  334.     {
  335.     char url[HTS_URLMAXSIZE*2];
  336.     char method[32];
  337.     char data[32768];
  338.     url[0]=method[0]=data[0]='\0';
  339.     //
  340.     printf("HTTP Proxy installed on %s:%d/\n", proxyAddr, proxyPort);
  341.     printf("ICP Proxy installed on %s:%d/\n", icpAddr, icpPort);
  342. #ifndef _WIN32
  343.     {
  344.       pid_t pid = getpid();
  345.       printf("PID=%d\n", (int)pid);
  346.     }
  347. #endif
  348.     fflush(stdout);
  349.     fflush(stderr);
  350.     //
  351.     if (!proxytrack_start(index, soc, socICP)) {
  352.       int last_errno = errno;
  353.       fprintf(stderr, "Unable to create the server: %s\n", strerror(last_errno));
  354. #ifdef _WIN32
  355.       closesocket(soc);
  356. #else
  357.       close(soc);
  358. #endif
  359.       printf("Done\n");
  360.       returncode = 1;
  361.     } else {
  362.       returncode = 0;
  363.     }
  364.   } else {
  365.     int last_errno = errno;
  366.         fprintf(stderr, "Unable to initialize a temporary server : %s\n", strerror(last_errno));
  367.     returncode = 1;
  368.   }
  369.   printf("EXITED\n");
  370.   fflush(stdout);
  371.   fflush(stderr);
  372.   return returncode;
  373. }
  374.  
  375. static const char* GetHttpMessage(int statuscode) {
  376.   // Erreurs HTTP, selon RFC
  377.   switch( statuscode) {    
  378.     case 100: return "Continue"; break; 
  379.         case 101: return "Switching Protocols"; break; 
  380.         case 200: return "OK"; break; 
  381.         case 201: return "Created"; break; 
  382.         case 202: return "Accepted"; break; 
  383.         case 203: return "Non-Authoritative Information"; break; 
  384.         case 204: return "No Content"; break; 
  385.         case 205: return "Reset Content"; break; 
  386.         case 206: return "Partial Content"; break; 
  387.         case 207: return "Multi-Status"; break; 
  388.         case 300: return "Multiple Choices"; break; 
  389.         case 301: return "Moved Permanently"; break; 
  390.         case 302: return "Moved Temporarily"; break; 
  391.         case 303: return "See Other"; break; 
  392.         case 304: return "Not Modified"; break; 
  393.         case 305: return "Use Proxy"; break; 
  394.         case 306: return "Undefined 306 error"; break; 
  395.         case 307: return "Temporary Redirect"; break; 
  396.         case 400: return "Bad Request"; break; 
  397.         case 401: return "Unauthorized"; break; 
  398.         case 402: return "Payment Required"; break; 
  399.         case 403: return "Forbidden"; break; 
  400.         case 404: return "Not Found"; break; 
  401.         case 405: return "Method Not Allowed"; break; 
  402.         case 406: return "Not Acceptable"; break; 
  403.         case 407: return "Proxy Authentication Required"; break; 
  404.         case 408: return "Request Time-out"; break; 
  405.         case 409: return "Conflict"; break; 
  406.         case 410: return "Gone"; break; 
  407.         case 411: return "Length Required"; break; 
  408.         case 412: return "Precondition Failed"; break; 
  409.         case 413: return "Request Entity Too Large"; break; 
  410.         case 414: return "Request-URI Too Large"; break; 
  411.         case 415: return "Unsupported Media Type"; break; 
  412.         case 416: return "Requested Range Not Satisfiable"; break; 
  413.         case 417: return "Expectation Failed"; break; 
  414.         case 500: return "Internal Server Error"; break; 
  415.         case 501: return "Not Implemented"; break; 
  416.         case 502: return "Bad Gateway"; break; 
  417.         case 503: return "Service Unavailable"; break; 
  418.         case 504: return "Gateway Time-out"; break; 
  419.         case 505: return "HTTP Version Not Supported"; break; 
  420.         default:    return "Unknown HTTP Error"; break; 
  421.     }
  422. }
  423.  
  424. #ifndef NO_WEBDAV
  425. static void proxytrack_add_DAV_Item(String *item, String *buff,
  426.                                                                         const char* filename,
  427.                                                                         size_t size,
  428.                                                                         time_t timestamp,
  429.                                                                         const char* mime,
  430.                                                                         int isDir,
  431.                                                                         int isRoot,
  432.                                                                         int isDefault) 
  433. {
  434.     struct tm * timetm;
  435.     if (timestamp == (time_t) 0 || timestamp == (time_t) -1) {
  436.         timestamp = time(NULL);
  437.     }
  438.     if ((timetm = gmtime(×tamp)) != NULL) {
  439.         char tms[256 + 1];
  440.         const char * name;
  441.         strftime(tms, 256, "%a, %d %b %Y %H:%M:%S GMT", timetm);        /* Sun, 18 Sep 2005 11:45:45 GMT */
  442.  
  443.         if (mime == NULL || *mime == 0)
  444.             mime = "application/octet-stream";
  445.  
  446.         StringLength(*buff) = 0;
  447.         escapexml(filename, buff);
  448.  
  449.         name = strrchr(StringBuff(*buff), '/');
  450.         if (name != NULL)
  451.             name++;
  452.         if (name == NULL || *name == 0) {
  453.             if (strcmp(mime, "text/html") == 0)
  454.                 name = "Default Document for the Folder.html";
  455.             else
  456.                 name = "Default Document for the Folder";
  457.         }
  458.  
  459.         StringRoom(*item, 1024);
  460.         sprintf(StringBuffRW(*item),
  461.             "<response xmlns=\"DAV:\">\r\n"
  462.             "<href>/webdav%s%s</href>\r\n"
  463.             "<propstat>\r\n"
  464.             "<prop>\r\n"
  465.             "<displayname>%s</displayname>\r\n"
  466.             "<iscollection>%d</iscollection>\r\n"
  467.             "<haschildren>%d</haschildren>\r\n"
  468.             "<isfolder>%d</isfolder>\r\n"
  469.             "<resourcetype>%s</resourcetype>\r\n"
  470.             "<creationdate>%d-%02d-%02dT%02d:%02d:%02dZ</creationdate>\r\n"
  471.             "<getlastmodified>%s</getlastmodified>\r\n"
  472.             "<supportedlock></supportedlock>\r\n"
  473.             "<lockdiscovery/>\r\n"
  474.             "<getcontenttype>%s</getcontenttype>\r\n"
  475.             "<getcontentlength>%d</getcontentlength>\r\n"
  476.             "<isroot>%d</isroot>\r\n"
  477.             "</prop>\r\n"
  478.             "<status>HTTP/1.1 200 OK</status>\r\n"
  479.             "</propstat>\r\n"
  480.             "</response>\r\n",
  481.             /* */
  482.             ( StringBuff(*buff)[0] == '/' ) ? "" : "/", StringBuff(*buff),
  483.             name,
  484.             isDir ? 1 : 0,
  485.             isDir ? 1 : 0,
  486.             isDir ? 1 : 0,
  487.             isDir ? "<collection/>" : "",
  488.             timetm->tm_year + 1900, timetm->tm_mon + 1, timetm->tm_mday, timetm->tm_hour, timetm->tm_min, timetm->tm_sec,
  489.             tms,
  490.             isDir ? "httpd/unix-directory" : mime,
  491.             (int)size,
  492.             isRoot ? 1 : 0
  493.             );
  494.         StringLength(*item) = (int) strlen(StringBuff(*item));
  495.     }
  496. }
  497.  
  498. /* Convert a RFC822 time to time_t */
  499. time_t get_time_rfc822(const char* s) {
  500.   struct tm result;
  501.   /* */
  502.   char months[]="jan feb mar apr may jun jul aug sep oct nov dec";
  503.   char str[256];
  504.   char* a;
  505.     int i;
  506.   /* */
  507.   int result_mm=-1;
  508.   int result_dd=-1;
  509.   int result_n1=-1;
  510.   int result_n2=-1;
  511.   int result_n3=-1;
  512.   int result_n4=-1;
  513.   /* */
  514.  
  515.   if ((int) strlen(s) > 200)
  516.     return (time_t)0;
  517.     for(i = 0 ; s[i] != 0 ; i++) {
  518.         if (s[i] >= 'A' && s[i] <= 'Z') 
  519.             str[i] = s[i] + ('a' - 'A');
  520.         else
  521.             str[i] = s[i];
  522.     }
  523.     str[i] = 0;
  524.   /* Θliminer :,- */
  525.   while( (a=strchr(str,'-')) ) *a=' ';
  526.   while( (a=strchr(str,':')) ) *a=' ';
  527.   while( (a=strchr(str,',')) ) *a=' ';
  528.   /* tokeniser */
  529.   a=str;
  530.   while(*a) {
  531.     char *first,*last;
  532.     char tok[256];
  533.     /* dΘcouper mot */
  534.     while(*a==' ') a++;   /* sauter espaces */
  535.     first=a;
  536.     while((*a) && (*a!=' ')) a++;
  537.     last=a;
  538.     tok[0]='\0';
  539.     if (first!=last) {
  540.       char* pos;
  541.       strncat(tok,first,(int) (last - first));
  542.       /* analyser */
  543.       if ( (pos=strstr(months,tok)) ) {               /* month always in letters */
  544.         result_mm=((int) (pos - months))/4;
  545.       } else {
  546.         int number;
  547.         if (sscanf(tok,"%d",&number) == 1) {      /* number token */
  548.           if (result_dd<0)                        /* day always first number */
  549.             result_dd=number;
  550.           else if (result_n1<0)
  551.             result_n1=number;
  552.           else if (result_n2<0)
  553.             result_n2=number;
  554.           else if (result_n3<0)
  555.             result_n3=number;
  556.           else if (result_n4<0)
  557.             result_n4=number;
  558.         }   /* sinon, bruit de fond(+1GMT for exampel) */
  559.       }
  560.     }
  561.   }
  562.   if ((result_n1>=0) && (result_mm>=0) && (result_dd>=0) && (result_n2>=0) && (result_n3>=0) && (result_n4>=0)) {
  563.     if (result_n4>=1000) {               /* Sun Nov  6 08:49:37 1994 */
  564.       result.tm_year=result_n4-1900;
  565.       result.tm_hour=result_n1;
  566.       result.tm_min=result_n2;
  567.       result.tm_sec=max(result_n3,0);
  568.     } else {                            /* Sun, 06 Nov 1994 08:49:37 GMT or Sunday, 06-Nov-94 08:49:37 GMT */
  569.       result.tm_hour=result_n2;
  570.       result.tm_min=result_n3;
  571.       result.tm_sec=max(result_n4,0);
  572.       if (result_n1<=50)                /* 00 means 2000 */
  573.         result.tm_year=result_n1+100;
  574.       else if (result_n1<1000)          /* 99 means 1999 */
  575.         result.tm_year=result_n1;
  576.       else                              /* 2000 */
  577.         result.tm_year=result_n1-1900;
  578.     }
  579.     result.tm_isdst=0;        /* assume GMT */
  580.     result.tm_yday=-1;        /* don't know */
  581.     result.tm_wday=-1;        /* don't know */
  582.     result.tm_mon=result_mm;
  583.     result.tm_mday=result_dd;
  584.     return mktime(&result);
  585.   }
  586.   return (time_t) 0;
  587. }
  588.  
  589. static PT_Element proxytrack_process_DAV_Request(PT_Indexes indexes, const char * urlFull, int depth) {
  590.     const char * file = jump_protocol_and_auth(urlFull);
  591.     if ( (file = strchr(file, '/')) == NULL)
  592.         return NULL;
  593.  
  594.     if (strncmp(file, "/webdav", 7) != 0) {
  595.         PT_Element elt = PT_ElementNew();
  596.         elt->statuscode = 405;
  597.         strcpy(elt->msg, "Method Not Allowed");
  598.         return elt;
  599.     }
  600.  
  601.     /* Skip /webdav */
  602.     file += 7;
  603.  
  604.     /* */
  605.     {
  606.         PT_Element elt = PT_ElementNew();
  607.         int i, isDir;
  608.         String url = STRING_EMPTY;
  609.         String response = STRING_EMPTY;
  610.         String item = STRING_EMPTY;
  611.         String itemUrl = STRING_EMPTY;
  612.         String buff = STRING_EMPTY;
  613.         StringClear(response);
  614.         StringClear(item);
  615.         StringClear(itemUrl);
  616.         StringClear(buff);
  617.  
  618.         /* Canonize URL */
  619.         StringCopy(url, file + ((file[0] == '/') ? 1 : 0));
  620.         if (StringLength(url) > 0) {
  621.             if (StringBuff(url)[StringLength(url) - 1] == '/') {
  622.                 StringBuffRW(url)[StringLength(url) - 1] = '\0';
  623.                 StringLength(url)--;
  624.             }
  625.         }
  626.  
  627.         /* Form response */
  628.         StringRoom(response, 1024);
  629.         sprintf(StringBuffRW(response),
  630.             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
  631.             "<multistatus xmlns=\"DAV:\">\r\n");
  632.         StringLength(response) = (int) strlen(StringBuff(response));
  633.         /* */
  634.  
  635.         /* Root */
  636.         StringLength(item) = 0;
  637.         proxytrack_add_DAV_Item(&item, &buff, 
  638.             StringBuff(url), /*size*/0, /*timestamp*/(time_t) 0, /*mime*/NULL, /*isDir*/1, /*isRoot*/1, /*isDefault*/0);
  639.         StringMemcat(response, StringBuff(item), StringLength(item));
  640.  
  641.         /* Childrens (Depth > 0) */
  642.         if (depth > 0) {
  643.             time_t timestampRep = (time_t) -1;
  644.             const char * prefix = StringBuff(url);
  645.             unsigned int prefixLen = (unsigned int) strlen(prefix);
  646.             char ** list = PT_Enumerate(indexes, prefix, 0);
  647.             if (list != NULL) {
  648.                 for(isDir = 1 ; isDir >= 0 ; isDir--) {
  649.                     for(i = 0 ; list[i] != NULL ; i++) {
  650.                         const char * thisUrl = list[i];
  651.                         const char * mimeType = "application/octet-stream";
  652.                         unsigned int thisUrlLen = (unsigned int) strlen(thisUrl);
  653.                         int thisIsDir = (thisUrl[thisUrlLen - 1] == '/') ? 1 : 0;
  654.  
  655.                         /* Item URL */
  656.                         StringRoom(itemUrl, thisUrlLen + prefixLen + sizeof("/webdav/") + 1);
  657.                         StringClear(itemUrl);
  658.                         sprintf(StringBuffRW(itemUrl), "/%s/%s", prefix, thisUrl);
  659.                         if (!thisIsDir)
  660.                             StringLength(itemUrl) = (int) strlen(StringBuff(itemUrl));
  661.                         else
  662.                             StringLength(itemUrl) = (int) strlen(StringBuff(itemUrl)) - 1;
  663.                         StringBuffRW(itemUrl)[StringLength(itemUrl)] = '\0';
  664.  
  665.                         if (thisIsDir == isDir) {
  666.                             size_t size = 0;
  667.                             time_t timestamp = (time_t) 0;
  668.                             PT_Element file = NULL;
  669.  
  670.                             /* Item stats */
  671.                             if (!isDir) {
  672.                                 file = PT_ReadIndex(indexes, StringBuff(itemUrl) + 1, FETCH_HEADERS);
  673.                                 if (file != NULL && file->statuscode == HTTP_OK ) {
  674.                                     size = file->size;
  675.                                     if (file->lastmodified) {
  676.                                         timestamp = get_time_rfc822(file->lastmodified);
  677.                                     }
  678.                                     if (timestamp == (time_t) 0) {
  679.                                         if (timestampRep == (time_t) -1) {
  680.                                             timestampRep = 0;
  681.                                             if (file->indexId != -1) {
  682.                                                 timestampRep = PT_Index_Timestamp(PT_GetIndex(indexes, file->indexId));
  683.                                             }
  684.                                         }
  685.                                         timestamp = timestampRep;
  686.                                     }
  687.                                     if (file->contenttype) {
  688.                                         mimeType = file->contenttype;
  689.                                     }
  690.                                 }
  691.                             }
  692.  
  693.                             /* Add item */
  694.                             StringLength(item) = 0;
  695.                             proxytrack_add_DAV_Item(&item, &buff, 
  696.                                 StringBuff(itemUrl), size, timestamp, mimeType, isDir, /*isRoot*/0, /*isDefault*/(thisUrlLen == 0));
  697.                             StringMemcat(response, StringBuff(item), StringLength(item));
  698.  
  699.                             /* Wipe element */
  700.                             if (file != NULL)
  701.                                 PT_Element_Delete(&file);
  702.                         }
  703.                     }
  704.                 }
  705.                 PT_Enumerate_Delete(&list);
  706.             }  /* items != NULL */
  707.         }        /* Depth > 0 */
  708.  
  709.         /* End of responses */
  710.         StringCat(response,
  711.             "</multistatus>\r\n"
  712.             );
  713.  
  714.         StringFree(item);
  715.         StringFree(itemUrl);
  716.         StringFree(url);
  717.         StringFree(buff);
  718.  
  719.         elt->size = StringLength(response);
  720.         elt->adr = StringAcquire(&response);
  721.         elt->statuscode = 207;                                    /* Multi-Status */
  722.         strcpy(elt->charset, "utf-8");
  723.         strcpy(elt->contenttype, "text/xml");
  724.         strcpy(elt->msg, "Multi-Status");
  725.         StringFree(response);
  726.  
  727.         fprintf(stderr, "RESPONSE:\n%s\n", elt->adr);
  728.  
  729.         return elt;
  730.     }
  731.     return NULL;
  732. }
  733. #endif
  734.  
  735. static PT_Element proxytrack_process_HTTP_List(PT_Indexes indexes, const char * url) {
  736.     char ** list = PT_Enumerate(indexes, url, 0);
  737.     if (list != NULL) {
  738.         PT_Element elt = PT_ElementNew();
  739.         int i, isDir;
  740.         String html = STRING_EMPTY;
  741.         StringClear(html);
  742.         StringCat(html, 
  743.             "<html>"
  744.             PROXYTRACK_COMMENT_HEADER
  745.             DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES
  746.             "<head>\r\n"
  747.             "<title>ProxyTrack " PROXYTRACK_VERSION " Catalog</title>"
  748.             "</head>\r\n"
  749.             "<body>\r\n"
  750.             "<h3>Directory index:</h3><br />"
  751.             "<br />"
  752.             "<hr>"
  753.             "<tt>[DIR] <a href=\"..\">..</a></tt><br />"
  754.             );
  755.         for(isDir = 1 ; isDir >= 0 ; isDir--) {
  756.             for(i = 0 ; list[i] != NULL ; i++) {
  757.                 char * thisUrl = list[i];
  758.                 unsigned int thisUrlLen = (unsigned int) strlen(thisUrl);
  759.                 int thisIsDir = (thisUrl[thisUrlLen - 1] == '/') ? 1 : 0;
  760.                 if (thisIsDir == isDir) {
  761.                     if (isDir)
  762.                         StringCat(html, "<tt>[DIR] ");
  763.                     else
  764.                         StringCat(html, "<tt>      ");
  765.                     StringCat(html, "<a href=\"");
  766.                     if (isDir) {
  767.                         StringCat(html, "http://proxytrack/");
  768.                     }
  769.                     StringCat(html, url);
  770.                     StringCat(html, list[i]);
  771.                     StringCat(html, "\">");
  772.                     StringCat(html, list[i]);
  773.                     StringCat(html, "</a></tt><br />");
  774.                 }
  775.             }
  776.         }
  777.         StringCat(html, 
  778.             "</body>"
  779.             "</html>");
  780.         PT_Enumerate_Delete(&list);
  781.         elt->size = StringLength(html);
  782.         elt->adr = StringAcquire(&html);
  783.         elt->statuscode = HTTP_OK;
  784.         strcpy(elt->charset, "iso-8859-1");
  785.         strcpy(elt->contenttype, "text/html");
  786.         strcpy(elt->msg, "OK");
  787.         StringFree(html);
  788.         return elt;
  789.     }
  790.     return NULL;
  791. }
  792.  
  793. static void proxytrack_process_HTTP(PT_Indexes indexes, T_SOC soc_c) {
  794.   int timeout=30;
  795.   int retour=0;
  796.     int willexit=0;
  797.     int buffer_size = 32768;
  798.     char * buffer = (char*)malloc(buffer_size);
  799.     int line1Size = 1024;
  800.     char * line1 = (char*)malloc(line1Size);
  801.     int lineSize = 8192;
  802.     char * line = (char*)malloc(lineSize);
  803.     int length = 0;
  804.     int keepAlive = 1;
  805.  
  806.     String url = STRING_EMPTY;
  807.     String urlRedirect = STRING_EMPTY;
  808.     String headers = STRING_EMPTY;
  809.     String output = STRING_EMPTY;
  810.     String host = STRING_EMPTY;
  811.     String localhost = STRING_EMPTY;
  812. #ifndef NO_WEBDAV
  813.     String davHeaders = STRING_EMPTY;
  814.     String davRequest = STRING_EMPTY;
  815. #endif
  816.  
  817.     StringRoom(localhost, 256);
  818.     if (gethostname(StringBuffRW(localhost), (int) StringCapacity(localhost) - 1) == 0) {
  819.         StringLength(localhost) = (int) strlen(StringBuff(localhost));
  820.     } else {
  821.         StringCopy(localhost, "localhost");
  822.     }
  823.  
  824. #ifdef _DEBUG
  825.     Sleep(1000);
  826. #endif
  827.  
  828.     if (buffer == NULL || line == NULL || line1 == NULL) {
  829.         CRITICAL("proxytrack_process_HTTP:memory exhausted");
  830. #ifdef _WIN32
  831.         closesocket(soc_c);
  832. #else
  833.         close(soc_c);
  834. #endif
  835.         return ;
  836.     }
  837.  
  838.     do {
  839.         const char* msgError = NULL;
  840.         int msgCode = 0;
  841.         PT_Element element = NULL;
  842.         char* command;
  843.         char* proto;
  844.         char* surl;
  845.         int directHit = 0;
  846.         int headRequest = 0;
  847.         int listRequest = 0;
  848. #ifndef NO_WEBDAV
  849.         int davDepth = 0;
  850. #endif
  851.  
  852.         /* Clear context */
  853.         line[0] = line1[0] = '\0';
  854.         buffer[0] = '\0';
  855.         command = line1;
  856.         StringClear(url);
  857.         StringClear(urlRedirect);
  858.         StringClear(headers);
  859.         StringClear(output);
  860.         StringClear(host);
  861. #ifndef NO_WEBDAV
  862.         StringClear(davHeaders);
  863.         StringClear(davRequest);
  864. #endif
  865.  
  866.         /* line1: "GET http://www.example.com/ HTTP/1.0" */
  867.         if (linputsoc_t(soc_c, line1, line1Size - 2, timeout) > 0
  868.             && ( surl = strchr(line1, ' ') )
  869.             && !(*surl = '\0') 
  870.             && ++surl 
  871.             && (proto = strchr(surl, ' ')) && !(*proto = '\0') && ++proto) 
  872.         {
  873.             /* Flush headers */
  874.             while(linputsoc_t(soc_c, line, lineSize - 2, timeout) > 0
  875.                 && line[0] != 0) 
  876.             {
  877.                 int p;
  878.                 if ((p = strfield(line, "Content-length:"))!=0) {
  879.                     if (sscanf(line+p, "%d", &length) != 1) {
  880.                         msgCode = 500;
  881.                         msgError = "Bad HTTP Content-Length Field";
  882.                         keepAlive = 0;
  883.                         length = 0;
  884.                     }
  885.                 } else if (strcasecmp(line, "Connection: close") == 0) {
  886.                     keepAlive = 0;
  887.                 } else if (strcasecmp(line, "Connection: keep-alive") == 0) {
  888.                     keepAlive = 1;
  889.                 } else if ((p = strfield(line, "Host:"))) {
  890.                     char* chost = line + p;
  891.                     if (*chost == ' ')
  892.                         chost++;
  893.                     StringCopy(host, chost);
  894.                 }
  895. #ifndef NO_WEBDAV
  896.                 else if ((p = strfield(line, "Depth: "))) {
  897.                     char* depth = line + p;
  898.                     if (sscanf(depth, "%d", &davDepth) != 1) {
  899.                         davDepth = 0;
  900.                     }
  901.                 }
  902. #endif
  903.             }
  904.  
  905.             /* Flush body */
  906. #ifndef NO_WEBDAV
  907.             if (length > 0) {
  908.                 if (length < 32768) {
  909.                     StringRoom(davRequest, length + 1);
  910.                     if (recv(soc_c, StringBuffRW(davRequest), length, 0) == length) {
  911.                         StringBuffRW(davRequest)[length] = 0;
  912.                     } else {
  913.                         msgCode = 500;
  914.                         msgError = "Posted Data Read Error";
  915.                         keepAlive = 0;
  916.                     }
  917.                 } else {
  918.                     msgCode = 500;
  919.                     msgError = "Posted Data Too Large";
  920.                     keepAlive = 0;
  921.                 }
  922.             }
  923. #endif
  924.  
  925.             /* Switch protocol ID */
  926.             if (strcasecmp(command, "post") == 0) {
  927. #ifndef NO_WEBDAV
  928.                 msgCode = 404;
  929. #else
  930.                 msgCode = 501;
  931.                 keepAlive = 0;
  932. #endif
  933.                 msgError = "Proxy Error (POST Request Forbidden)";
  934.             }
  935.             else if (strcasecmp(command, "get") == 0) {
  936.                 headRequest = 0;
  937.             }
  938.             else if (strcasecmp(command, "head") == 0) {
  939.                 headRequest = 1;
  940.             }
  941. #ifndef NO_WEBDAV
  942.             else if (strcasecmp(command, "options") == 0) {
  943.                 const char * options = "GET, HEAD, OPTIONS, POST, PROPFIND, TRACE"
  944.                     ", MKCOL, DELETE, PUT";            /* Not supported */
  945.                 msgCode = HTTP_OK;
  946.                 StringRoom(headers, 8192);
  947.                 sprintf(StringBuffRW(headers), 
  948.                     "HTTP/1.1 %d %s\r\n"
  949.                     "DAV: 1, 2\r\n"
  950.                     "MS-Author-Via: DAV\r\n"
  951.                     "Cache-Control: private\r\n"
  952.                     "Allow: %s\r\n",
  953.                     msgCode, GetHttpMessage(msgCode), options);
  954.                 StringLength(headers) = (int) strlen(StringBuff(headers));
  955.             }
  956.             else if (strcasecmp(command, "propfind") == 0) {
  957.                 if (davDepth > 1) {
  958.                     msgCode = 403;
  959.                     msgError = "DAV Depth Limit Forbidden";
  960.                 } else {
  961.                     fprintf(stderr, "DEBUG: DAV-DATA=<%s>\n", StringBuff(davRequest));
  962.                     listRequest = 2;                /* propfind */
  963.                 }
  964.             }
  965.             else if (strcasecmp(command, "mkcol") == 0
  966.                 || strcasecmp(command, "delete") == 0
  967.                 || strcasecmp(command, "put") == 0
  968.                 || strcasecmp(command, "proppatch") == 0
  969.                 || strcasecmp(command, "lock") == 0
  970.                 || strcasecmp(command, "unlock") == 0
  971.                 || strcasecmp(command, "copy") == 0
  972.                 || strcasecmp(command, "trace") == 0)
  973.             {
  974.                 msgCode = 403;
  975.                 msgError = "Method Forbidden";
  976.             }
  977. #endif
  978.             else {
  979.                 msgCode = 501;
  980.                 msgError = "Proxy Error (Unsupported or Unknown HTTP Command Request)";
  981.                 keepAlive = 0;
  982.             }
  983.             if (strcasecmp(proto, "http/1.1") == 0) {
  984.                 keepAlive = 1;
  985.             } else if (strcasecmp(proto, "http/1.0") == 0) {
  986.                 keepAlive = 0;
  987.             } else {
  988.                 msgCode = 505;
  989.                 msgError = "Proxy Error (Unknown HTTP Version)";
  990.                 keepAlive = 0;
  991.             }
  992.  
  993.             /* Post-process request */
  994.             if (link_has_authority(surl)) {
  995.                 if (strncasecmp(surl, "http://proxytrack/", sizeof("http://proxytrack/") - 1) == 0) {
  996.                     directHit = 1; /* Another direct hit hack */
  997.                 }
  998.                 StringCopy(url, surl);
  999.             } else {
  1000.                 if (StringLength(host) > 0) {
  1001.                     /* Direct hit */
  1002.                     if (
  1003. #ifndef NO_WEBDAV
  1004.                         listRequest != 2
  1005.                         &&
  1006. #endif
  1007.                         strncasecmp(StringBuff(host), StringBuff(localhost), StringLength(localhost)) == 0
  1008.                         && 
  1009.                         (StringBuff(host)[StringLength(localhost)] == '\0'
  1010.                         || StringBuff(host)[StringLength(localhost)] == ':')
  1011.                         && surl[0] == '/'
  1012.                         )
  1013.                     {
  1014.                         const char * toHit = surl + 1;
  1015.                         if (strncmp(toHit, "webdav/", 7) == 0) {
  1016.                             toHit += 7;
  1017.                         }
  1018.                         /* Direct hit */
  1019.                         directHit = 1;
  1020.                         StringCopy(url, "");
  1021.                         if (!link_has_authority(toHit))
  1022.                             StringCat(url, "http://");
  1023.                         StringCat(url, toHit);
  1024.                     } else if (strncasecmp(surl, "/proxytrack/", sizeof("/proxytrack/") - 1) == 0) {
  1025.                         const char * toHit = surl + sizeof("/proxytrack/") - 1;
  1026.                         /* Direct hit */
  1027.                         directHit = 1;
  1028.                         StringCopy(url, "");
  1029.                         if (!link_has_authority(toHit))
  1030.                             StringCat(url, "http://");
  1031.                         StringCat(url, toHit);
  1032.                     } else {
  1033.                         /* Transparent proxy */
  1034.                         StringCopy(url, "http://");
  1035.                         StringCat(url, StringBuff(host));
  1036.                         StringCat(url, surl);
  1037.                     }
  1038.                 } else {
  1039.                     msgCode = 500;
  1040.                     msgError = "Transparent Proxy Error ('Host' HTTP Request Header Field Missing)";
  1041.                     keepAlive = 0;
  1042.                 }
  1043.             }
  1044.  
  1045.             /* Response */
  1046.             if (msgCode == 0) {
  1047.                 if (listRequest == 1) {
  1048.                     element = proxytrack_process_HTTP_List(indexes, StringBuff(url));
  1049.                 }
  1050. #ifndef NO_WEBDAV
  1051.                 else if (listRequest == 2) {
  1052.                     if ((element = proxytrack_process_DAV_Request(indexes, StringBuff(url), davDepth)) != NULL) {
  1053.                         msgCode = element->statuscode;
  1054.                         StringRoom(davHeaders, 1024);
  1055.                         sprintf(StringBuffRW(davHeaders), 
  1056.                             "DAV: 1, 2\r\n"
  1057.                             "MS-Author-Via: DAV\r\n"
  1058.                             "Cache-Control: private\r\n");
  1059.                         StringLength(davHeaders) = (int) strlen(StringBuff(davHeaders));
  1060.                     }
  1061.                 }
  1062. #endif
  1063.                 else {
  1064.                     element = PT_ReadIndex(indexes, StringBuff(url), FETCH_BODY);
  1065.                 }
  1066.                 if (element == NULL
  1067. #ifndef NO_WEBDAV
  1068.                     && listRequest == 2
  1069. #endif
  1070.                     && StringLength(url) > 0 
  1071.                     && StringBuff(url)[StringLength(url) - 1] == '/'
  1072.                     )
  1073.                 {
  1074.                     element = PT_Index_HTML_BuildRootInfo(indexes);
  1075.                     if (element != NULL) {
  1076.                         element->statuscode = 404;                /* HTML page, but in error */
  1077.                     }
  1078.                 }
  1079.                 if (element != NULL) {
  1080.                     msgCode = element->statuscode;
  1081.                     StringRoom(headers, 8192);
  1082.                     sprintf(StringBuffRW(headers), 
  1083.                         "HTTP/1.1 %d %s\r\n"
  1084. #ifndef NO_WEBDAV
  1085.                         "%s"
  1086. #endif
  1087.                         "Content-Type: %s%s%s%s\r\n"
  1088.                         "%s%s%s"
  1089.                         "%s%s%s"
  1090.                         "%s%s%s",
  1091.                         /* */
  1092.                         msgCode,
  1093.                         element->msg,
  1094. #ifndef NO_WEBDAV
  1095.                         /* DAV */
  1096.                         StringBuff(davHeaders),
  1097. #endif
  1098.                         /* Content-type: foo; [ charset=bar ] */
  1099.                         element->contenttype,
  1100.                         ( ( element->charset[0]) ? "; charset=\"" : ""),
  1101.                         element->charset,
  1102.                         ( ( element->charset[0]) ? "\"" : ""),
  1103.                         /* location */
  1104.                         ( ( element->location != NULL && element->location[0]) ? "Location: " : ""),
  1105.                         ( ( element->location != NULL && element->location[0]) ? element->location : ""),
  1106.                         ( ( element->location != NULL && element->location[0]) ? "\r\n" : ""),
  1107.                         /* last-modified */
  1108.                         ( ( element->lastmodified[0]) ? "Last-Modified: " : ""),
  1109.                         ( ( element->lastmodified[0]) ? element->lastmodified : ""),
  1110.                         ( ( element->lastmodified[0]) ? "\r\n" : ""),
  1111.                         /* etag */
  1112.                         ( ( element->etag[0]) ? "ETag: " : ""),
  1113.                         ( ( element->etag[0]) ? element->etag : ""),
  1114.                         ( ( element->etag[0]) ? "\r\n" : "")
  1115.                         );
  1116.                     StringLength(headers) = (int) strlen(StringBuff(headers));
  1117.                 } else {
  1118.                     /* No query string, no ending / : check the the <url>/ page */
  1119.                     if (StringLength(url) > 0 && StringBuff(url)[StringLength(url) - 1] != '/' && strchr(StringBuff(url), '?') == NULL) {
  1120.                         StringCopy(urlRedirect, StringBuff(url));
  1121.                         StringCat(urlRedirect, "/");
  1122.                         if (PT_LookupIndex(indexes, StringBuff(urlRedirect))) {
  1123.                             msgCode = 301;  /* Moved Permanently */
  1124.                             StringRoom(headers, 8192);
  1125.                             sprintf(StringBuffRW(headers),
  1126.                                 "HTTP/1.1 %d %s\r\n"
  1127.                                 "Content-Type: text/html\r\n"
  1128.                                 "Location: %s\r\n",
  1129.                                 /* */
  1130.                                 msgCode,
  1131.                                 GetHttpMessage(msgCode),
  1132.                                 StringBuff(urlRedirect)
  1133.                                 );
  1134.                             StringLength(headers) = (int) strlen(StringBuff(headers));
  1135.                             /* */
  1136.                             StringRoom(output, 1024 + sizeof(PROXYTRACK_COMMENT_HEADER) + sizeof(DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES));
  1137.                             sprintf(StringBuffRW(output), 
  1138.                                 "<html>"
  1139.                                 PROXYTRACK_COMMENT_HEADER
  1140.                                 DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES
  1141.                                 "<head>"
  1142.                                 "<title>ProxyTrack - Page has moved</title>"
  1143.                                 "</head>\r\n"
  1144.                                 "<body>"
  1145.                                 "<h3>The correct location is:</h3><br />"
  1146.                                 "<b><a href=\"%s\">%s</a></b><br />"
  1147.                                 "<br />"
  1148.                                 "<br />\r\n"
  1149.                                 "<i>Generated by ProxyTrack " PROXYTRACK_VERSION ", (C) Xavier Roche and other contributors</i>"
  1150.                                 "\r\n"
  1151.                                 "</body>"
  1152.                                 "</header>",
  1153.                                 StringBuff(urlRedirect),
  1154.                                 StringBuff(urlRedirect));
  1155.                             StringLength(output) = (int) strlen(StringBuff(output));
  1156.                         }
  1157.                     }
  1158.                     if (msgCode == 0) {
  1159.                         msgCode = 404;
  1160.                         msgError = "Not Found in this cache";
  1161.                     }
  1162.                 }
  1163.             }
  1164.         } else {
  1165.             msgCode = 500;
  1166.             msgError = "Server Error";
  1167.             keepAlive = 0;
  1168.         }
  1169.         if (StringLength(headers) == 0) {
  1170.             if (msgCode == 0) {
  1171.                 msgCode = 500;
  1172.                 msgError = "Internal Proxy Error";
  1173.             } else if (msgError == NULL) {
  1174.                 msgError = GetHttpMessage(msgCode);
  1175.             }
  1176.             StringRoom(headers, 256);
  1177.             sprintf(StringBuffRW(headers), 
  1178.                 "HTTP/1.1 %d %s\r\n"
  1179.                 "Content-type: text/html\r\n",
  1180.                 msgCode,
  1181.                 msgError);
  1182.             StringLength(headers) = (int) strlen(StringBuff(headers));
  1183.             StringRoom(output, 1024 + sizeof(PROXYTRACK_COMMENT_HEADER) + sizeof(DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES));
  1184.             sprintf(StringBuffRW(output), 
  1185.                 "<html>"
  1186.                 PROXYTRACK_COMMENT_HEADER
  1187.                 DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES
  1188.                 "<head>"
  1189.                 "<title>ProxyTrack - HTTP Proxy Error %d</title>"
  1190.                 "</head>\r\n"
  1191.                 "<body>"
  1192.                 "<h3>A proxy error has occured while processing the request.</h3><br />"
  1193.                 "<b>Error HTTP %d: <i>%s</i></b><br />"
  1194.                 "<br />"
  1195.                 "<br />\r\n"
  1196.                 "<i>Generated by ProxyTrack " PROXYTRACK_VERSION ", (C) Xavier Roche and other contributors</i>"
  1197.                 "\r\n"
  1198.                 "</body>"
  1199.                 "</html>",
  1200.                 msgCode,
  1201.                 msgCode,
  1202.                 msgError);
  1203.             StringLength(output) = (int) strlen(StringBuff(output));
  1204.         }
  1205.         {
  1206.             char tmp[20 + 1]; /* 2^64 = 18446744073709551616 */
  1207.             size_t dataSize = 0;
  1208.             if (!headRequest) {
  1209.                 dataSize = StringLength(output);
  1210.                 if (dataSize == 0 && element != NULL) {
  1211.                     dataSize = element->size;
  1212.                 }
  1213.             }
  1214.             sprintf(tmp, "%d", (int) dataSize);
  1215.             StringCat(headers, "Content-length: ");
  1216.             StringCat(headers, tmp);
  1217.             StringCat(headers, "\r\n");
  1218.         }
  1219.         if (keepAlive) {
  1220.             StringCat(headers, 
  1221.                 "Connection: Keep-Alive\r\n"
  1222.                 "Proxy-Connection: Keep-Alive\r\n");
  1223.         } else {
  1224.             StringCat(headers, 
  1225.                 "Connection: Close\r\n"
  1226.                 "Proxy-Connection: Close\r\n");
  1227.         }
  1228.         if (msgCode != 500)
  1229.             StringCat(headers, "X-Cache: HIT from ");
  1230.         else
  1231.             StringCat(headers, "X-Cache: MISS from ");
  1232.         StringCat(headers, StringBuff(localhost));
  1233.         StringCat(headers, "\r\n");
  1234.  
  1235.         /* Logging */
  1236.         {
  1237.             const char * contentType = "text/html";
  1238.             size_t size = StringLength(output) ? StringLength(output) : ( element ? element->size : 0 );
  1239.             /* */
  1240.             String ip = STRING_EMPTY;
  1241.             SOCaddr serverClient;
  1242.             int lenServerClient = (int) sizeof(serverClient);
  1243.             memset(&serverClient, 0, sizeof(serverClient));
  1244.             if (getsockname(soc_c, (struct sockaddr*) &serverClient, &lenServerClient) == 0) {
  1245.                 ip = getip(&serverClient, lenServerClient);
  1246.             } else {
  1247.                 StringCopy(ip, "unknown");
  1248.             }
  1249.             if (element != NULL && element->contenttype[0] != '\0') {
  1250.                 contentType = element->contenttype;
  1251.             }
  1252.             LOG("HTTP %s %d %d %s %s %s" _ StringBuff(ip) _ msgCode _ (int)size _ command _ StringBuff(url) _ contentType);
  1253.             StringFree(ip);
  1254.         }
  1255.  
  1256.         /* Send reply */
  1257.         StringCat(headers, "Server: ProxyTrack " PROXYTRACK_VERSION " (HTTrack " HTTRACK_VERSIONID ")\r\n");
  1258.         StringCat(headers, "\r\n");            /* Headers separator */
  1259.         if (send(soc_c, StringBuff(headers), (int)StringLength(headers), 0) != StringLength(headers)
  1260.             || ( !headRequest && StringLength(output) > 0 && send(soc_c, StringBuff(output), (int)StringLength(output), 0) != StringLength(output))
  1261.             || ( !headRequest && StringLength(output) == 0 && element != NULL && element->adr != NULL && send(soc_c, element->adr, (int)element->size, 0) != element->size)
  1262.             )
  1263.         {
  1264.             keepAlive = 0;        /* Error, abort connection */
  1265.         }
  1266.         PT_Element_Delete(&element);
  1267.  
  1268.         /* Shutdown (FIN) and wait until confirmed */
  1269.         if (!keepAlive) {
  1270.             char c;
  1271. #ifdef _WIN32
  1272.             shutdown(soc_c, SD_SEND);
  1273. #else
  1274.             shutdown(soc_c, 1);
  1275. #endif
  1276.             while(recv(soc_c, ((char*)&c), 1, 0) > 0);
  1277.         }
  1278.     } while(keepAlive);
  1279.  
  1280. #ifdef _WIN32
  1281.     closesocket(soc_c);
  1282. #else
  1283.     close(soc_c);
  1284. #endif
  1285.  
  1286.     StringFree(url);
  1287.     StringFree(urlRedirect);
  1288.     StringFree(headers);
  1289.     StringFree(output);
  1290.     StringFree(host);
  1291.  
  1292.     if (buffer)
  1293.         free(buffer);
  1294. }
  1295.  
  1296. /* Generic threaded function start */
  1297. static int startThread(void (*funct)(void*), void* param) 
  1298. {
  1299.     if (param != NULL) {
  1300. #ifdef _WIN32
  1301.         if (_beginthread(funct, 0, param) == -1) {
  1302.             free(param);
  1303.             return 0;
  1304.         }
  1305.         return 1;
  1306. #else
  1307.         pthread_t handle = 0;
  1308.         int retcode;
  1309.         retcode = pthread_create(&handle, NULL, funct, param);
  1310.         if (retcode != 0) {   /* error */
  1311.             free(param);
  1312.             return 0;
  1313.         } else {
  1314.             /* detach the thread from the main process so that is can be independent */
  1315.             pthread_detach(handle);
  1316.             return 1;
  1317.         }
  1318. #endif
  1319.     } else {
  1320.         return 0;
  1321.     }
  1322. }
  1323.  
  1324. /* Generic socket/index structure */
  1325. typedef struct proxytrack_process_th_p {
  1326.     T_SOC soc_c;
  1327.     PT_Indexes indexes;
  1328.     void (*process)(PT_Indexes indexes, T_SOC soc_c);
  1329. } proxytrack_process_th_p;
  1330.  
  1331. /* Generic socket/index function stub */
  1332. static void proxytrack_process_th(void* param_) {
  1333.     proxytrack_process_th_p *param = (proxytrack_process_th_p *) param_;
  1334.     T_SOC soc_c = param->soc_c;
  1335.     PT_Indexes indexes = param->indexes;
  1336.     void (*process)(PT_Indexes indexes, T_SOC soc_c) = param->process;
  1337.     free(param);
  1338.     process(indexes, soc_c);
  1339.     return ;
  1340. }
  1341.  
  1342. /* Process generic socket/index operation */
  1343. static int proxytrack_process_generic(void (*process)(PT_Indexes indexes, T_SOC soc_c),
  1344.                                                                             PT_Indexes indexes, T_SOC soc_c) 
  1345. {
  1346.     proxytrack_process_th_p *param = calloc(sizeof(proxytrack_process_th_p), 1);
  1347.     if (param != NULL) {
  1348.         param->soc_c = soc_c;
  1349.         param->indexes = indexes;
  1350.         param->process = process;
  1351.         return startThread(proxytrack_process_th, param);
  1352.     } else {
  1353.         CRITICAL("proxytrack_process_generic:Memory exhausted");
  1354.         return 0;
  1355.     }
  1356.     return 0;
  1357. }
  1358.  
  1359. /* Process HTTP proxy requests */
  1360. static int proxytrack_process_HTTP_threaded(PT_Indexes indexes, T_SOC soc) {
  1361.     return proxytrack_process_generic(proxytrack_process_HTTP, indexes, soc);
  1362. }
  1363.  
  1364. /* HTTP Server */
  1365. static int proxytrack_start_HTTP(PT_Indexes indexes, T_SOC soc) {
  1366.     while(soc != INVALID_SOCKET) {
  1367.     T_SOC soc_c;
  1368.         struct sockaddr clientAddr;
  1369.         int clientAddrLen = sizeof(struct sockaddr);
  1370.         memset(&clientAddr, 0, sizeof(clientAddr));
  1371.         if ( (soc_c = (T_SOC) accept(soc, &clientAddr, &clientAddrLen)) != INVALID_SOCKET) {
  1372.             if (!proxytrack_process_HTTP_threaded(indexes, soc_c)) {
  1373.                 CRITICAL("proxytrack_start_HTTP::Can not fork a thread");
  1374.             }
  1375.         }
  1376.     }
  1377.     if (soc != INVALID_SOCKET) {
  1378. #ifdef _WIN32
  1379.         closesocket(soc);
  1380. #else
  1381.         close(soc);
  1382. #endif
  1383.     }
  1384.     return 1;
  1385. }
  1386.  
  1387. /* Network order is big endian */
  1388. #define READ_NET16(buffer) ( ( ((unsigned char*)buffer)[0] << 8 ) + ((unsigned char*)buffer)[1] )
  1389. #define READ_NET32(buffer) ( ( READ_NET16(buffer) << 16 ) + READ_NET16(((unsigned char*)buffer) + 2) )
  1390. #define WRITE_NET8(buffer, value) do { \
  1391.     ((unsigned char*)buffer)[0] = (unsigned char)(value); \
  1392. } while(0)
  1393. #define WRITE_NET16(buffer, value) do { \
  1394.     ((unsigned char*)buffer)[0] = (((unsigned short)(value)) >> 8) & 0xff; \
  1395.     ((unsigned char*)buffer)[1] = ((unsigned short)(value)) & 0xff; \
  1396. } while(0)
  1397. #define WRITE_NET32(buffer, value) do { \
  1398.     WRITE_NET16(buffer, ( ((unsigned int)(value)) >> 16 ) & 0xffff); \
  1399.     WRITE_NET16(((unsigned char*)buffer) + 2, ( ((unsigned int)(value)) ) & 0xffff); \
  1400. } while(0)
  1401.  
  1402. static int ICP_reply(struct sockaddr * clientAddr,
  1403.                                          int clientAddrLen,
  1404.                                          T_SOC soc,
  1405.                                          /* */
  1406.                                          unsigned char  Opcode,
  1407.                                          unsigned char  Version,
  1408.                                          unsigned short Message_Length,
  1409.                                          unsigned int   Request_Number,
  1410.                                          unsigned int   Options,
  1411.                                          unsigned int   Option_Data,
  1412.                                          unsigned int   Sender_Host_Address,
  1413.                                          unsigned char *Message
  1414.                                          ) 
  1415. {
  1416.     int ret = 0;
  1417.     unsigned long int BufferSize;
  1418.     unsigned char * buffer;
  1419.     if (Message_Length == 0 && Message != NULL)                                /* We have to get the message size */
  1420.         Message_Length  = (unsigned int) strlen(Message) + 1;        /* NULL terminated */
  1421.     BufferSize = 20 + Message_Length;
  1422.     buffer = malloc(BufferSize);
  1423.     if (buffer != NULL) {
  1424.         WRITE_NET8(&buffer[0], Opcode);
  1425.         WRITE_NET8(&buffer[1], Version);
  1426.         WRITE_NET16(&buffer[2], Message_Length);
  1427.         WRITE_NET32(&buffer[4], Request_Number);
  1428.         WRITE_NET32(&buffer[8], Options);
  1429.         WRITE_NET32(&buffer[12], Option_Data);
  1430.         WRITE_NET32(&buffer[16], Sender_Host_Address);
  1431.         if (Message != NULL && Message_Length > 0) {
  1432.             memcpy(buffer + 20, Message, Message_Length);
  1433.         }
  1434.         if (sendto(soc, buffer, BufferSize, 0, clientAddr, clientAddrLen) == BufferSize) {
  1435.             ret = 1;
  1436.         }
  1437.         free(buffer);
  1438.     }
  1439.     return ret;
  1440. }
  1441.  
  1442. /* ICP Server */
  1443. static int proxytrack_start_ICP(PT_Indexes indexes, T_SOC soc) {
  1444.     /* "ICP messages MUST not exceed 16,384 octets in length." (RFC2186) */
  1445.     int bufferSize = 16384;
  1446.     unsigned char * buffer = (unsigned char*) malloc(bufferSize + 1);
  1447.     if (buffer == NULL) {
  1448.         CRITICAL("proxytrack_start_ICP:memory exhausted");
  1449. #ifdef _WIN32
  1450.         closesocket(soc);
  1451. #else
  1452.         close(soc);
  1453. #endif
  1454.         return -1;
  1455.     }
  1456.     while(soc != INVALID_SOCKET) {
  1457.         struct sockaddr clientAddr;
  1458.         int clientAddrLen = sizeof(struct sockaddr);
  1459.         int n;
  1460.         memset(&clientAddr, 0, sizeof(clientAddr));
  1461.         n = recvfrom(soc, (char*)buffer, bufferSize, 0, &clientAddr, &clientAddrLen);
  1462.         if (n != -1) {
  1463.             const char * LogRequest = "ERROR";
  1464.             const char * LogReply = "ERROR";
  1465.             unsigned char * UrlRequest = NULL;
  1466.             if (n >= 20) {
  1467.                 enum {
  1468.                     ICP_OP_MIN = 0,
  1469.                     ICP_OP_INVALID = 0,
  1470.                     ICP_OP_QUERY = 1,
  1471.                     ICP_OP_HIT = 2,
  1472.                     ICP_OP_MISS = 3,
  1473.                     ICP_OP_ERR = 4,
  1474.                     ICP_OP_SECHO = 10,
  1475.                     ICP_OP_DECHO = 11,
  1476.                     ICP_OP_MISS_NOFETCH = 21,
  1477.                     ICP_OP_DENIED = 22,
  1478.                     ICP_OP_HIT_OBJ = 23,
  1479.                     ICP_OP_MAX = ICP_OP_HIT_OBJ
  1480.                 };
  1481.                 unsigned char  Opcode = buffer[0];
  1482.                 unsigned char  Version = buffer[1];
  1483.                 unsigned short Message_Length = READ_NET16(&buffer[2]);
  1484.                 unsigned int   Request_Number = READ_NET32(&buffer[4]);   /* Session ID */
  1485.                 unsigned int   Options = READ_NET32(&buffer[8]);
  1486.                 unsigned int   Option_Data = READ_NET32(&buffer[12]);     /* ICP_FLAG_SRC_RTT */
  1487.                 unsigned int   Sender_Host_Address = READ_NET32(&buffer[16]);        /* ignored */
  1488.                 unsigned char* Payload = &buffer[20];
  1489.                 buffer[bufferSize] = '\0';                                                              /* Ensure payload is NULL terminated */
  1490.                 if (Message_Length <= bufferSize - 20) {
  1491.                     if (Opcode >= ICP_OP_MIN && Opcode <= ICP_OP_MAX) {
  1492.                         if (Version == 2) {
  1493.                             switch(Opcode) {
  1494.                             case ICP_OP_QUERY:
  1495.                                 {
  1496.                                     unsigned int UrlRequestSize;
  1497.                                     UrlRequest = &Payload[4];
  1498.                                     UrlRequestSize = (unsigned int)strlen((char*)UrlRequest);
  1499.                                     LogRequest = "ICP_OP_QUERY";
  1500.                                     if (indexes == NULL) {
  1501.                                         ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_DENIED, Version, 0, Request_Number, 0, 0, 0, UrlRequest);
  1502.                                         LogReply = "ICP_OP_DENIED";
  1503.                                     } else if (PT_LookupIndex(indexes, UrlRequest)) {
  1504.                                         ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_HIT, Version, 0, Request_Number, 0, 0, 0, UrlRequest);
  1505.                                         LogReply = "ICP_OP_HIT";
  1506.                                     } else {
  1507.                                         if (UrlRequestSize > 0 && UrlRequest[UrlRequestSize - 1] != '/' && strchr(UrlRequest, '?') == NULL) {
  1508.                                             char * UrlRedirect = malloc(UrlRequestSize + 1 + 1);
  1509.                                             if (UrlRedirect != NULL) {
  1510.                                                 sprintf(UrlRedirect, "%s/", UrlRequest);
  1511.                                                 if (PT_LookupIndex(indexes, UrlRedirect)) {            /* We'll generate a redirect */
  1512.                                                     ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_HIT, Version, 0, Request_Number, 0, 0, 0, UrlRequest);
  1513.                                                     LogReply = "ICP_OP_HIT";
  1514.                                                     free(UrlRedirect);
  1515.                                                     break;
  1516.                                                 }
  1517.                                                 free(UrlRedirect);
  1518.                                             }
  1519.                                         }
  1520.                                         /* We won't retrive the cache MISS online, no way! */
  1521.                                         ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_MISS_NOFETCH, Version, 0, Request_Number, 0, 0, 0, UrlRequest);
  1522.                                         LogReply = "ICP_OP_MISS_NOFETCH";
  1523.                                     }
  1524.                                 }
  1525.                                 break;
  1526.                             case ICP_OP_SECHO:
  1527.                                 {
  1528.                                     UrlRequest = &Payload[4];
  1529.                                     LogRequest = "ICP_OP_QUERY";
  1530.                                     LogReply = "ICP_OP_QUERY";
  1531.                                     ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_SECHO, Version, 0, Request_Number, 0, 0, 0, UrlRequest);
  1532.                                 }
  1533.                                 break;
  1534.                             default:
  1535.                                 LogRequest = "NOTIMPLEMENTED";
  1536.                                 LogReply = "ICP_OP_ERR";
  1537.                                 ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_ERR, Version, 0, Request_Number, 0, 0, 0, NULL);
  1538.                                 break;
  1539.                             }
  1540.                         } else {
  1541.                             ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_ERR, 2, 0, Request_Number, 0, 0, 0, NULL);
  1542.                         }
  1543.                     } /* Ignored (RFC2186) */
  1544.                 } else {
  1545.                     ICP_reply(&clientAddr, clientAddrLen, soc, ICP_OP_ERR, Version, 0, Request_Number, 0, 0, 0, NULL);
  1546.                 }
  1547.             }
  1548.  
  1549.             /* Logging */
  1550.             {
  1551.                 String ip = STRING_EMPTY;
  1552.                 SOCaddr serverClient;
  1553.                 int lenServerClient = (int) sizeof(serverClient);
  1554.                 SOCaddr_copyaddr(serverClient, lenServerClient, &clientAddr, clientAddrLen);
  1555.                 if (lenServerClient > 0) {
  1556.                     ip = getip(&serverClient, lenServerClient);
  1557.                 } else {
  1558.                     StringCopy(ip, "unknown");
  1559.                 }
  1560.                 LOG("ICP %s %s/%s %s" _ StringBuff(ip) _ LogRequest _ LogReply _ (UrlRequest ? UrlRequest : "-") );
  1561.                 StringFree(ip);
  1562.             }
  1563.  
  1564.         }
  1565.     }
  1566.     if (soc != INVALID_SOCKET) {
  1567. #ifdef _WIN32
  1568.         closesocket(soc);
  1569. #else
  1570.         close(soc);
  1571. #endif
  1572.     }
  1573.     free(buffer);
  1574.     return 1;
  1575. }
  1576.  
  1577. static int proxytrack_start(PT_Indexes indexes, T_SOC soc, T_SOC socICP) {
  1578.     int ret = 1;
  1579.     if (proxytrack_process_generic(proxytrack_start_ICP, indexes, socICP)) {
  1580.         //if (!proxytrack_process_generic(proxytrack_start_HTTP, indexes, soc))
  1581.         if (!proxytrack_start_HTTP(indexes, soc)) {
  1582.             ret = 0;
  1583.         }
  1584.     } else {
  1585.         ret = 0;
  1586.     }
  1587.     return ret;
  1588. }
  1589.  
  1590.